// Filename: transformState.I
// Created by:  drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
//     Function: TransformState::operator <
//       Access: Published
//  Description: Provides an arbitrary ordering among all unique
//               TransformStates, so we can store the essentially
//               different ones in a big set and throw away the rest.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
operator < (const TransformState &other) const {
  return compare_to(other) < 0;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::compare_to
//       Access: Published
//  Description: Provides an arbitrary ordering among all unique
//               TransformStates, so we can store the essentially
//               different ones in a big set and throw away the rest.
////////////////////////////////////////////////////////////////////
INLINE int TransformState::
compare_to(const TransformState &other) const {
  return compare_to(other, uniquify_matrix);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_hash
//       Access: Published
//  Description: Returns a suitable hash value for phash_map.
////////////////////////////////////////////////////////////////////
INLINE size_t TransformState::
get_hash() const {
  check_hash();
  return _hash;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_pos
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_pos(const LVecBase3 &pos) {
  return make_pos_hpr_scale(pos, 
                            LVecBase3(0.0f, 0.0f, 0.0f),
                            LVecBase3(1.0f, 1.0f, 1.0f));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_hpr
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_hpr(const LVecBase3 &hpr) {
  return make_pos_hpr_scale(LVecBase3(0.0f, 0.0f, 0.0f),
                            hpr, 
                            LVecBase3(1.0f, 1.0f, 1.0f));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_quat
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_quat(const LQuaternion &quat) {
  return make_pos_quat_scale(LVecBase3(0.0f, 0.0f, 0.0f),
                             quat, 
                             LVecBase3(1.0f, 1.0f, 1.0f));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_pos_hpr
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_pos_hpr(const LVecBase3 &pos, const LVecBase3 &hpr) {
  return make_pos_hpr_scale(pos, hpr,
                            LVecBase3(1.0, 1.0f, 1.0f));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_scale
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_scale(PN_stdfloat scale) {
  // We actually map this 3-d uniform make_scale() to the 2-d
  // version--might as well call it a 2-d scale.
  return make_scale2d(scale);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_scale
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_scale(const LVecBase3 &scale) {
  return make_pos_hpr_scale(LVecBase3(0.0f, 0.0f, 0.0f),
                            LVecBase3(0.0f, 0.0f, 0.0f),
                            scale);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_shear
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_shear(const LVecBase3 &shear) {
  return make_pos_hpr_scale_shear(LVecBase3(0.0f, 0.0f, 0.0f),
                                  LVecBase3(0.0f, 0.0f, 0.0f),
                                  LVecBase3(1.0f, 1.0f, 1.0f),
                                  shear);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_pos_hpr_scale
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_pos_hpr_scale(const LVecBase3 &pos, const LVecBase3 &hpr, 
                   const LVecBase3 &scale) {
  return make_pos_hpr_scale_shear(pos, hpr, scale, LVecBase3::zero());
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_pos_quat_scale
//       Access: Published, Static
//  Description: Makes a new TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_pos_quat_scale(const LVecBase3 &pos, const LQuaternion &quat, 
                    const LVecBase3 &scale) {
  return make_pos_quat_scale_shear(pos, quat, scale, LVecBase3::zero());
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_pos2d
//       Access: Published, Static
//  Description: Makes a new 2-d TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_pos2d(const LVecBase2 &pos) {
  return make_pos_rotate_scale2d(pos, 0.0f, LVecBase2(1.0f, 1.0f));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_rotate2d
//       Access: Published, Static
//  Description: Makes a new 2-d TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_rotate2d(PN_stdfloat rotate) {
  return make_pos_rotate_scale2d(LVecBase2(0.0f, 0.0f), rotate, 
                                 LVecBase2(1.0f, 1.0f));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_pos_rotate2d
//       Access: Published, Static
//  Description: Makes a new 2-d TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_pos_rotate2d(const LVecBase2 &pos, PN_stdfloat rotate) {
  return make_pos_rotate_scale2d(pos, rotate,
                                 LVecBase2(1.0, 1.0f));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_scale2d
//       Access: Published, Static
//  Description: Makes a new 2-d TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_scale2d(PN_stdfloat scale) {
  return make_pos_rotate_scale2d(LVecBase2(0.0f, 0.0f), 0.0f,
                                 LVecBase2(scale, scale));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_scale2d
//       Access: Published, Static
//  Description: Makes a new 2-d TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_scale2d(const LVecBase2 &scale) {
  return make_pos_rotate_scale2d(LVecBase2(0.0f, 0.0f), 0.0f, scale);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_shear2d
//       Access: Published, Static
//  Description: Makes a new 2-d TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_shear2d(PN_stdfloat shear) {
  return make_pos_rotate_scale_shear2d(LVecBase2(0.0f, 0.0f), 0.0f, 
                                       LVecBase2(1.0f, 1.0f), shear);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::make_pos_rotate_scale2d
//       Access: Published, Static
//  Description: Makes a new 2-d TransformState with the specified
//               components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_pos_rotate_scale2d(const LVecBase2 &pos, PN_stdfloat rotate, 
                        const LVecBase2 &scale) {
  return make_pos_rotate_scale_shear2d(pos, rotate, scale, 0.0f);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::is_identity
//       Access: Published
//  Description: Returns true if the transform represents the identity
//               matrix, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
is_identity() const {
  return ((_flags & F_is_identity) != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::is_invalid
//       Access: Published
//  Description: Returns true if the transform represents an invalid
//               matrix, for instance the result of inverting a
//               singular matrix, or false if the transform is valid.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
is_invalid() const {
  return ((_flags & F_is_invalid) != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::is_singular
//       Access: Published
//  Description: Returns true if the transform represents a singular
//               transform (that is, it has a zero scale, and it
//               cannot be inverted), or false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
is_singular() const {
  check_singular();
  return ((_flags & F_is_singular) != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::is_2d
//       Access: Published
//  Description: Returns true if the transform has been constructed
//               entirely using the 2-d transform operations,
//               e.g. make_pos2d(), and therefore operates strictly in
//               two-dimensional space on X and Y only.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
is_2d() const {
  return ((_flags & F_is_2d) != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_components
//       Access: Published
//  Description: Returns true if the transform can be described by
//               separate pos, hpr, and scale components.  Most
//               transforms we use in everyday life can be so
//               described, but some kinds of transforms (for
//               instance, those involving a skew) cannot.
//
//               This is not related to whether the transform was
//               originally described componentwise.  Even a transform
//               that was constructed with a 4x4 may return true here
//               if the matrix is a simple affine matrix with no skew.
//
//               If this returns true, you may safely call get_hpr()
//               and get_scale() to retrieve the components.  (You
//               may always safely call get_pos() whether this returns
//               true or false.)
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_components() const {
  check_components();
  return ((_flags & F_has_components) != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::components_given
//       Access: Published
//  Description: Returns true if the transform was specified
//               componentwise, or false if it was specified with a
//               general 4x4 matrix.  If this is true, the components
//               returned by get_pos() and get_scale() will be exactly
//               those that were set; otherwise, these functions will
//               return computed values.  If this is true, the
//               rotation may have been set either with a hpr trio or
//               with a quaternion; hpr_given() or quat_given() can
//               resolve the difference.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
components_given() const {
  return ((_flags & F_components_given) != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::hpr_given
//       Access: Published
//  Description: Returns true if the rotation was specified via a trio
//               of Euler angles, false otherwise.  If this is true,
//               get_hpr() will be exactly as set; otherwise, it will
//               return a computed value.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
hpr_given() const {
  return ((_flags & F_hpr_given) != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::quat_given
//       Access: Published
//  Description: Returns true if the rotation was specified via a 
//               quaternion, false otherwise.  If this is true,
//               get_quat() will be exactly as set; otherwise, it will
//               return a computed value.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
quat_given() const {
  return ((_flags & F_quat_given) != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_pos
//       Access: Published
//  Description: Returns true if the transform's pos component can be
//               extracted out separately.  This is generally always
//               true, unless the transform is invalid
//               (i.e. is_invalid() returns true).
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_pos() const {
  return !is_invalid();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_hpr
//       Access: Published
//  Description: Returns true if the transform's rotation component
//               can be extracted out separately and described as a
//               set of Euler angles.  This is generally true only
//               when has_components() is true.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_hpr() const {
  return has_components();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_quat
//       Access: Published
//  Description: Returns true if the transform's rotation component
//               can be extracted out separately and described as a
//               quaternion.  This is generally true only when
//               has_components() is true.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_quat() const {
  return has_components();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_scale
//       Access: Published
//  Description: Returns true if the transform's scale component
//               can be extracted out separately.  This is generally
//               true only when has_components() is true.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_scale() const {
  return has_components();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_identity_scale
//       Access: Published
//  Description: Returns true if the scale is uniform 1.0, or false if
//               the scale has some real value.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_identity_scale() const {
  check_components();
  return (_flags & F_identity_scale) != 0;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_uniform_scale
//       Access: Published
//  Description: Returns true if the scale is uniform across all three
//               axes (and therefore can be expressed as a single
//               number), or false if the transform has a different
//               scale in different dimensions.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_uniform_scale() const {
  check_components();
  return (_flags & F_uniform_scale) != 0;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_shear
//       Access: Published
//  Description: Returns true if the transform's shear component
//               can be extracted out separately.  This is generally
//               true only when has_components() is true.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_shear() const {
  return has_components();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_nonzero_shear
//       Access: Published
//  Description: Returns true if the shear component is non-zero,
//               false if it is zero or if the matrix cannot be
//               decomposed.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_nonzero_shear() const {
  check_components();
  return (_flags & F_has_nonzero_shear) != 0;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::has_mat
//       Access: Published
//  Description: Returns true if the transform can be described as a
//               matrix.  This is generally always true, unless
//               is_invalid() is true.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
has_mat() const {
  return !is_invalid();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_pos
//       Access: Published
//  Description: Returns the pos component of the transform.  It is an
//               error to call this if has_pos() returned false.
////////////////////////////////////////////////////////////////////
INLINE const LPoint3 &TransformState::
get_pos() const {
  check_components();
  nassertr(has_pos(), _pos);
  return _pos;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_hpr
//       Access: Published
//  Description: Returns the rotation component of the transform as a
//               trio of Euler angles.  It is an error to call this if
//               has_components() returned false.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase3 &TransformState::
get_hpr() const {
  check_hpr();
  nassertr(!is_invalid(), _hpr);
  return _hpr;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_quat
//       Access: Published
//  Description: Returns the rotation component of the transform as a
//               quaternion.  The return value will be normalized if a
//               normalized quaternion was given to the constructor
//               (or if the quaternion was computed implicitly); it
//               will be non-normalized if a non-normalized quaternion
//               was given to the constructor.  See also
//               get_norm_quat().
//
//               It is an error to call this if has_components()
//               returned false.
////////////////////////////////////////////////////////////////////
INLINE const LQuaternion &TransformState::
get_quat() const {
  check_quat();
  nassertr(!is_invalid(), _quat);
  return _quat;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_norm_quat
//       Access: Published
//  Description: Returns the rotation component of the transform as a
//               quaternion.  Unlike the result of get_quat(), the
//               return value of this method is guaranteed to be
//               normalized.  It is an error to call this if
//               has_components() returned false.
////////////////////////////////////////////////////////////////////
INLINE const LQuaternion &TransformState::
get_norm_quat() const {
  check_norm_quat();
  nassertr(!is_invalid(), _norm_quat);
  return _norm_quat;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_scale
//       Access: Published
//  Description: Returns the scale component of the transform.  It is an
//               error to call this if has_components() returned
//               false.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase3 &TransformState::
get_scale() const {
  check_components();
  nassertr(!is_invalid(), _scale);
  return _scale;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_uniform_scale
//       Access: Published
//  Description: Returns the scale component of the transform, as a
//               single number.  It is an error to call this if
//               has_uniform_scale() returned false.
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat TransformState::
get_uniform_scale() const {
  check_components();
  nassertr(has_uniform_scale(), _scale[0]);
  return _scale[0];
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_shear
//       Access: Published
//  Description: Returns the shear component of the transform.  It is
//               an error to call this if has_components() returned
//               false.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase3 &TransformState::
get_shear() const {
  check_components();
  nassertr(!is_invalid(), _shear);
  return _shear;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_mat
//       Access: Published
//  Description: Returns the matrix that describes the transform.
////////////////////////////////////////////////////////////////////
INLINE const LMatrix4 &TransformState::
get_mat() const {
  nassertr(has_mat(), LMatrix4::ident_mat());
  check_mat();
  return _mat;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_pos2d
//       Access: Published
//  Description: Returns the pos component of the 2-d transform.  It
//               is an error to call this if has_pos() or is_2d()
//               returned false.
////////////////////////////////////////////////////////////////////
INLINE LVecBase2 TransformState::
get_pos2d() const {
  check_components();
  nassertr(has_pos() && is_2d(), LVecBase2::zero());
  return LVecBase2(_pos[0], _pos[1]);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_rotate2d
//       Access: Published
//  Description: Returns the rotation component of the 2-d transform
//               as an angle in degrees clockwise about the origin.
//               It is an error to call this if has_components() or
//               is_2d() returned false.
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat TransformState::
get_rotate2d() const {
  check_hpr();
  nassertr(!is_invalid() && is_2d(), 0);
  switch (get_default_coordinate_system()) {
  default:
  case CS_zup_right:
    return _hpr[0];
  case CS_zup_left:
    return -_hpr[0];
  case CS_yup_right:
    return -_hpr[2];
  case CS_yup_left:
    return _hpr[2];
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_scale2d
//       Access: Published
//  Description: Returns the scale component of the 2-d transform.  It
//               is an error to call this if has_components() or
//               is_2d() returned false.
////////////////////////////////////////////////////////////////////
INLINE LVecBase2 TransformState::
get_scale2d() const {
  check_components();
  nassertr(!is_invalid() && is_2d(), LVecBase2::zero());
  return LVecBase2(_scale[0], _scale[1]);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_shear2d
//       Access: Published
//  Description: Returns the shear component of the 2-d transform.  It
//               is an error to call this if has_components() or
//               is_2d() returned false.
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat TransformState::
get_shear2d() const {
  check_components();
  nassertr(!is_invalid() && is_2d(), 0.0f);
  return _shear[0];
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_mat3
//       Access: Published
//  Description: Returns the 3x3 matrix that describes the 2-d
//               transform.  It is an error to call this if is_2d()
//               returned false.
////////////////////////////////////////////////////////////////////
INLINE LMatrix3 TransformState::
get_mat3() const {
  nassertr(has_mat() && is_2d(), LMatrix3::ident_mat());
  check_mat();
  return LMatrix3(_mat(0, 0), _mat(0, 1), _mat(0, 3),
                   _mat(1, 0), _mat(1, 1), _mat(1, 3),
                   _mat(3, 0), _mat(3, 1), _mat(3, 3));
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_inverse
//       Access: Published
//  Description: Returns the inverse of this transform.  If you are
//               going to immediately compose this result with another
//               TransformState, it is faster to do it in one
//               operation with invert_compose().
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
get_inverse() const {
  return invert_compose(TransformState::make_identity());
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_unique
//       Access: Published
//  Description: Returns the pointer to the unique TransformState in
//               the cache that is equivalent to this one.  This may
//               be the same pointer as this object, or it may be a
//               different pointer; but it will be an equivalent
//               object, and it will be a shared pointer.  This may be
//               called from time to time to improve cache benefits.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
get_unique() const {
  return return_unique((TransformState *)this);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_geom_rendering
//       Access: Published
//  Description: Returns the union of the Geom::GeomRendering bits
//               that will be required once this TransformState is
//               applied to a geom which includes the indicated
//               geom_rendering bits.  The RenderState's
//               get_geom_rendering() should already have been
//               applied.
////////////////////////////////////////////////////////////////////
INLINE int TransformState::
get_geom_rendering(int geom_rendering) const {
  if ((geom_rendering & GeomEnums::GR_point_perspective) != 0) {
    if (!has_identity_scale()) {
      geom_rendering |= GeomEnums::GR_point_scale;
    }
  }

  return geom_rendering;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::cache_ref
//       Access: Published
//  Description: Overrides this method to update PStats appropriately.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
cache_ref() const {
#ifdef DO_PSTATS
  int old_referenced_bits = get_referenced_bits();
  NodeCachedReferenceCount::cache_ref();
  consider_update_pstats(old_referenced_bits);
#else  // DO_PSTATS
  NodeCachedReferenceCount::cache_ref();
#endif  // DO_PSTATS
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::cache_unref
//       Access: Published
//  Description: Overrides this method to update PStats appropriately.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
cache_unref() const {
#ifdef DO_PSTATS
  int old_referenced_bits = get_referenced_bits();
  bool result = do_cache_unref();
  consider_update_pstats(old_referenced_bits);
  return result;
#else  // DO_PSTATS
  return do_cache_unref();
#endif  // DO_PSTATS
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::node_ref
//       Access: Published
//  Description: Overrides this method to update PStats appropriately.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
node_ref() const {
#ifdef DO_PSTATS
  int old_referenced_bits = get_referenced_bits();
  NodeCachedReferenceCount::node_ref();
  consider_update_pstats(old_referenced_bits);
#else  // DO_PSTATS
  NodeCachedReferenceCount::node_ref();
#endif  // DO_PSTATS
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::node_unref
//       Access: Published
//  Description: Overrides this method to update PStats appropriately.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
node_unref() const {
#ifdef DO_PSTATS
  int old_referenced_bits = get_referenced_bits();
  bool result = do_node_unref();
  consider_update_pstats(old_referenced_bits);
  return result;
#else  // DO_PSTATS
  return do_node_unref();
#endif  // DO_PSTATS
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_composition_cache_num_entries
//       Access: Published
//  Description: Returns the number of entries in the composition
//               cache for this TransformState.  This is the number of
//               other TransformStates whose composition with this one
//               has been cached.  This number is not useful for any
//               practical reason other than performance analysis.
////////////////////////////////////////////////////////////////////
INLINE int TransformState::
get_composition_cache_num_entries() const {
  LightReMutexHolder holder(*_states_lock);
  return _composition_cache.get_num_entries();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_invert_composition_cache_num_entries
//       Access: Published
//  Description: Returns the number of entries in the
//               invert_composition cache for this TransformState.
//               This is similar to the composition cache, but it
//               records cache entries for the invert_compose()
//               operation.  See get_composition_cache_num_entries().
////////////////////////////////////////////////////////////////////
INLINE int TransformState::
get_invert_composition_cache_num_entries() const {
  LightReMutexHolder holder(*_states_lock);
  return _invert_composition_cache.get_num_entries();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_composition_cache_size
//       Access: Published
//  Description: Returns the number of slots in the composition
//               cache for this TransformState.  You may use this as
//               an upper bound when walking through all of the
//               composition cache results via
//               get_composition_cache_source() or result().
//
//               This has no practical value other than for examining
//               the cache for performance analysis.
////////////////////////////////////////////////////////////////////
INLINE int TransformState::
get_composition_cache_size() const {
  LightReMutexHolder holder(*_states_lock);
  return _composition_cache.get_size();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_composition_cache_source
//       Access: Published
//  Description: Returns the source TransformState of the nth element
//               in the composition cache.  Returns NULL if there
//               doesn't happen to be an entry in the nth element.
//               See get_composition_cache_result().
//
//               This has no practical value other than for examining
//               the cache for performance analysis.
////////////////////////////////////////////////////////////////////
INLINE const TransformState *TransformState::
get_composition_cache_source(int n) const {
  LightReMutexHolder holder(*_states_lock);
  if (!_composition_cache.has_element(n)) {
    return NULL;
  }
  return _composition_cache.get_key(n);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_composition_cache_result
//       Access: Published
//  Description: Returns the result TransformState of the nth element
//               in the composition cache.  Returns NULL if there
//               doesn't happen to be an entry in the nth element.
//
//               In general,
//               a->compose(a->get_composition_cache_source(n)) ==
//               a->get_composition_cache_result(n).
//
//               This has no practical value other than for examining
//               the cache for performance analysis.
////////////////////////////////////////////////////////////////////
INLINE const TransformState *TransformState::
get_composition_cache_result(int n) const {
  LightReMutexHolder holder(*_states_lock);
  if (!_composition_cache.has_element(n)) {
    return NULL;
  }
  return _composition_cache.get_data(n)._result;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_invert_composition_cache_size
//       Access: Published
//  Description: Returns the number of slots in the composition
//               cache for this TransformState.  You may use this as
//               an upper bound when walking through all of the
//               composition cache results via
//               get_invert_composition_cache_source() or result().
//
//               This has no practical value other than for examining
//               the cache for performance analysis.
////////////////////////////////////////////////////////////////////
INLINE int TransformState::
get_invert_composition_cache_size() const {
  LightReMutexHolder holder(*_states_lock);
  return _invert_composition_cache.get_size();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_invert_composition_cache_source
//       Access: Published
//  Description: Returns the source TransformState of the nth element
//               in the invert composition cache.  Returns NULL if
//               there doesn't happen to be an entry in the nth
//               element.  See get_invert_composition_cache_result().
//
//               This has no practical value other than for examining
//               the cache for performance analysis.
////////////////////////////////////////////////////////////////////
INLINE const TransformState *TransformState::
get_invert_composition_cache_source(int n) const {
  LightReMutexHolder holder(*_states_lock);
  if (!_invert_composition_cache.has_element(n)) {
    return NULL;
  }
  return _invert_composition_cache.get_key(n);
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::get_invert_composition_cache_result
//       Access: Published
//  Description: Returns the result TransformState of the nth element
//               in the invert composition cache.  Returns NULL if
//               there doesn't happen to be an entry in the nth
//               element.
//
//               In general,
//               a->invert_compose(a->get_invert_composition_cache_source(n))
//               == a->get_invert_composition_cache_result(n).
//
//               This has no practical value other than for examining
//               the cache for performance analysis.
////////////////////////////////////////////////////////////////////
INLINE const TransformState *TransformState::
get_invert_composition_cache_result(int n) const {
  LightReMutexHolder holder(*_states_lock);
  if (!_invert_composition_cache.has_element(n)) {
    return NULL;
  }
  return _invert_composition_cache.get_data(n)._result;
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::flush_level
//       Access: Public, Static
//  Description: Flushes the PStatCollectors used during traversal.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
flush_level() {
  _node_counter.flush_level();
  _cache_counter.flush_level();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::do_node_unref
//       Access: Private
//  Description: Reimplements NodeReferenceCount::node_unref().  We do
//               this because we have a non-virtual unref() method.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
do_node_unref() const {
  node_unref_only();
  return unref();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::do_cache_unref
//       Access: Private
//  Description: Reimplements
//               CachedTypedWritableReferenceCount::cache_unref().  We
//               do this because we have a non-virtual unref() method.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
do_cache_unref() const {
  cache_unref_only();
  return unref();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_hash
//       Access: Private
//  Description: Ensures that we know the hash value.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_hash() const {
  // This pretends to be a const function, even though it's not,
  // because it only updates a transparent cache value.
  if ((_flags & F_hash_known) == 0) {
    ((TransformState *)this)->calc_hash();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_singular
//       Access: Private
//  Description: Ensures that we know whether the matrix is singular.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_singular() const {
  // This pretends to be a const function, even though it's not,
  // because it only updates a transparent cache value.
  if ((_flags & F_singular_known) == 0) {
    ((TransformState *)this)->calc_singular();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_components
//       Access: Private
//  Description: Ensures that we know the components of the transform
//               (or that we know they cannot be derived).
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_components() const {
  // This pretends to be a const function, even though it's not,
  // because it only updates a transparent cache value.
  if ((_flags & F_components_known) == 0) {
    ((TransformState *)this)->calc_components();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_hpr
//       Access: Private
//  Description: Ensures that we know the hpr of the transform
//               (or that we know they cannot be derived).
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_hpr() const {
  // This pretends to be a const function, even though it's not,
  // because it only updates a transparent cache value.
  if ((_flags & F_hpr_known) == 0) {
    ((TransformState *)this)->calc_hpr();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_quat
//       Access: Private
//  Description: Ensures that we know the quat of the transform
//               (or that we know they cannot be derived).
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_quat() const {
  // This pretends to be a const function, even though it's not,
  // because it only updates a transparent cache value.
  if ((_flags & F_quat_known) == 0) {
    ((TransformState *)this)->calc_quat();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_norm_quat
//       Access: Private
//  Description: Ensures that we know the normalized quat of the transform
//               (or that we know they cannot be derived).
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_norm_quat() const {
  // This pretends to be a const function, even though it's not,
  // because it only updates a transparent cache value.
  if ((_flags & F_norm_quat_known) == 0) {
    ((TransformState *)this)->calc_norm_quat();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_mat
//       Access: Private
//  Description: Ensures that we know the overall matrix.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_mat() const {
  // This pretends to be a const function, even though it's not,
  // because it only updates a transparent cache value.
  if ((_flags & F_mat_known) == 0) {
    ((TransformState *)this)->calc_mat();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::calc_hash
//       Access: Private
//  Description: Computes the hash value.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
calc_hash() {
  LightMutexHolder holder(_lock);
  do_calc_hash();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::calc_components
//       Access: Private
//  Description: Derives the components from the matrix, if possible.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
calc_components() {
  LightMutexHolder holder(_lock);
  do_calc_components();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::calc_hpr
//       Access: Private
//  Description: Derives the hpr, from the matrix if necessary, or
//               from the quat.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
calc_hpr() {
  LightMutexHolder holder(_lock);
  do_calc_hpr();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::calc_mat
//       Access: Private
//  Description: Computes the matrix from the components.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
calc_mat() {
  LightMutexHolder holder(_lock);
  do_calc_mat();
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_uniform_scale
//       Access: Private
//  Description: Should be called immediately after _scale (and
//               F_has_components) is set, this checks for a
//               identity and/or uniform scale (as well as a non-zero
//               shear) and sets the bit appropriately.
//
//               It does not matter whether the lock is or is not held
//               before calling this method.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_uniform_scale() {
  if (IS_NEARLY_EQUAL(_scale[0], _scale[1]) && 
      IS_NEARLY_EQUAL(_scale[0], _scale[2])) {
    _flags |= F_uniform_scale;
    if (IS_NEARLY_EQUAL(_scale[0], 1.0f)) {
      _flags |= F_identity_scale;
    }
  }

  if (!_shear.almost_equal(LVecBase3::zero())) {
    _flags |= F_has_nonzero_shear;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::check_uniform_scale2d
//       Access: Private
//  Description: Should be called immediately after _scale (and
//               F_has_components) is set, for a known 2-d scale, this
//               checks for a identity and/or uniform scale (as well
//               as a non-zero shear) and sets the bit appropriately.
//
//               It does not matter whether the lock is or is not held
//               before calling this method.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
check_uniform_scale2d() {
  if (IS_NEARLY_EQUAL(_scale[0], _scale[1])) {
    _scale[2] = _scale[0];
    _flags |= F_uniform_scale;
    if (IS_NEARLY_EQUAL(_scale[0], 1.0f)) {
      _flags |= F_identity_scale;
    }
  }

  if (!_shear.almost_equal(LVecBase3::zero())) {
    _flags |= F_has_nonzero_shear;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::set_destructing
//       Access: Private
//  Description: This function should only be called from the
//               destructor; it indicates that this TransformState
//               object is beginning destruction.  It is only used as
//               a sanity check, and is only meaningful when NDEBUG is
//               not defined.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
set_destructing() {
#ifndef NDEBUG
  _flags |= F_is_destructing;
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::is_destructing
//       Access: Private
//  Description: Returns true if the TransformState object is
//               currently within its destructor
//               (i.e. set_destructing() has been called).  This is
//               only used as a sanity check, and is only meaningful
//               when NDEBUG is not defined.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
is_destructing() const {
#ifndef NDEBUG
  return (_flags & F_is_destructing) != 0;
#else
  return false;
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::consider_update_pstats
//       Access: Private
//  Description: Calls update_pstats() if the state of the referenced
//               bits has changed from the indicated value.
////////////////////////////////////////////////////////////////////
INLINE void TransformState::
consider_update_pstats(int old_referenced_bits) const {
#ifdef DO_PSTATS
  int new_referenced_bits = get_referenced_bits();
  if (old_referenced_bits != new_referenced_bits) {
    update_pstats(old_referenced_bits, new_referenced_bits);
  }
#endif  // DO_PSTATS
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::Composition::Constructor
//       Access: Public
//  Description: 
////////////////////////////////////////////////////////////////////
INLINE TransformState::Composition::
Composition() {
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::Composition::Copy Constructor
//       Access: Public
//  Description: 
////////////////////////////////////////////////////////////////////
INLINE TransformState::Composition::
Composition(const TransformState::Composition &copy) :
  _result(copy._result)
{
}

////////////////////////////////////////////////////////////////////
//     Function: TransformState::CompositionCycleDescEntry::Constructor
//       Access: Public
//  Description: 
////////////////////////////////////////////////////////////////////
INLINE TransformState::CompositionCycleDescEntry::
CompositionCycleDescEntry(const TransformState *obj,
                          const TransformState *result,
                          bool inverted) :
  _obj(obj),
  _result(result),
  _inverted(inverted)
{
}
